#include "appender_console_color_ansi.h"

#include "formater_pattern.h"
#include "os.h"

namespace afcore {
namespace log {

template<typename ConsoleMutex>
CAppenderAnsicolor<ConsoleMutex>::CAppenderAnsicolor(FILE* file, EAppenderColorMode mode)
  : file_(file)
  , mutex_(ConsoleMutex::mutex())
  , formatter_(std::make_unique<CFormaterPattern>()) {
  SetColorMode(mode);
  colors_[kLogLevelTrace] = white;
  colors_[kLogLevelDebug] = cyan;
  colors_[kLogLevelInfo] = green;
  colors_[kLogLevelWarn] = yellow_bold;
  colors_[kLogLevelError] = red_bold;
  colors_[kLogLevelFatal] = bold_on_red;
  colors_[kLogLevelOff] = reset;
}

template<typename ConsoleMutex>
void CAppenderAnsicolor<ConsoleMutex>::SetColor(ELogLevel l, RStringView color) {
  std::lock_guard<RMutex> lock(mutex_);
  colors_[l] = color;
}

template<typename ConsoleMutex>
void CAppenderAnsicolor<ConsoleMutex>::Log(const SLogMessage &msg) {
  std::lock_guard<RMutex> lock(mutex_);
  msg.color_range_start = 0;
  msg.color_range_end = 0;
  RMemoryBuf formatted;
  formatter_->Format(msg, formatted);
  if (should_do_colors_ && msg.color_range_end > msg.color_range_start) {
    PrintRange(formatted, 0, msg.color_range_start);
    PrintColorCode(colors_[msg.level]);
    PrintRange(formatted, msg.color_range_start, msg.color_range_end);
    PrintColorCode(reset);
    PrintRange(formatted, msg.color_range_end, formatted.size());
  } else {
    PrintRange(formatted, 0, formatted.size());
  }
  fflush(file_);
}

template<typename ConsoleMutex>
void CAppenderAnsicolor<ConsoleMutex>::Flush() {
  std::lock_guard<RMutex> lock(mutex_);
  fflush(file_);
}

template<typename ConsoleMutex>
void CAppenderAnsicolor<ConsoleMutex>::SetPattern(const std::string &pattern) {
  std::lock_guard<RMutex> lock(mutex_);
  formatter_ = std::unique_ptr<CFormatter>(new CFormaterPattern(pattern));
}

template<typename ConsoleMutex>
void CAppenderAnsicolor<ConsoleMutex>::SetFormatter(RFormatterUptr formatter) {
  std::lock_guard<RMutex> lock(mutex_);
  formatter_ = std::move(formatter);
}

template<typename ConsoleMutex>
void CAppenderAnsicolor<ConsoleMutex>::SetColorMode(EAppenderColorMode mode) {
  switch (mode) {
    case EAppenderColorMode::kAppenderColorMode_Always:
      {
        should_do_colors_ = true;
      }
      break;
    case EAppenderColorMode::kAppenderColorMode_Automatic:
      {
        should_do_colors_ = InTerminal(file_) && IsColorTerminal();
      }
      break;
    case EAppenderColorMode::kAppenderColorMode_Never:
      {
        should_do_colors_ = false;
      }
      break;
  }
}

template<typename ConsoleMutex>
bool CAppenderAnsicolor<ConsoleMutex>::ShouldColor() {
  return should_do_colors_;
}

template<typename ConsoleMutex>
void CAppenderAnsicolor<ConsoleMutex>::PrintColorCode(const RStringView &color_code) {
  fwrite(color_code.data(), sizeof(char), color_code.size(), file_);
}

template<typename ConsoleMutex>
void CAppenderAnsicolor<ConsoleMutex>::PrintRange(const RMemoryBuf &formatted, size_t start, size_t end) {
  fwrite(formatted.data() + start, sizeof(char), end - start, file_);
}

template<typename ConsoleMutex>
CAppenderAnsicolorStdout<ConsoleMutex>::CAppenderAnsicolorStdout(EAppenderColorMode mode)
  : CAppenderAnsicolor<ConsoleMutex>(stdout, mode) {
}

template<typename ConsoleMutex>
CAppenderAnsicolorStderr<ConsoleMutex>::CAppenderAnsicolorStderr(EAppenderColorMode mode)
  : CAppenderAnsicolor<ConsoleMutex>(stderr, mode) {
}

template class CAppenderAnsicolor<SConsoleMutex>;
template class CAppenderAnsicolor<SConsoleNullMutex>;
template class CAppenderAnsicolorStdout<SConsoleMutex>;
template class CAppenderAnsicolorStdout<SConsoleNullMutex>;
template class CAppenderAnsicolorStderr<SConsoleMutex>;
template class CAppenderAnsicolorStderr<SConsoleNullMutex>;

} // !namespace log
} // !namespace afcore